/********************************************************************************
 * @file    bsp_gpio.c
 * @author  jianqiang.xue
 * @version V1.0.0
 * @date    2021-04-09
 * @brief   gpio初始化
 ********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdbool.h>
#include <stdint.h>

#include "cx32l003_hal_def.h"
#include "cx32l003_hal_gpio.h"
#include "cx32l003_hal_conf.h"
#include "cx32l003.h"

#include "bsp_exti.h"
#include "bsp_gpio.h"

/* Private Includes ----------------------------------------------------------*/
/* Private Define ------------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
/* Private Function Prototypes -----------------------------------------------*/
static uint8_t get_exti_event(bsp_gpio_exti_int_event_t exti_type) {
    if (BSP_GPIO_EXTI_INT_LOWFALL == exti_type)
        return GPIO_EXTI_INT_LOWFALL;
    else if (BSP_GPIO_EXTI_INT_HIGHRISE == exti_type)
        return GPIO_EXTI_INT_HIGHRISE;
    else if (BSP_GPIO_EXTI_INT_FALLRISE == exti_type)
        return GPIO_EXTI_INT_FALLRISE;
    else
        return GPIO_EXTI_INT_FALLRISE;
}
/* Public Function Prototypes ------------------------------------------------*/
/**
 * @brief  [反初始化] 关闭指定引脚功能(恢复为浮空输入)
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 */
void bsp_gpio_deinit(void* gpiox, uint8_t pin) {
    HAL_GPIO_DeInit(gpiox, pin);
}

/**
 * @brief  [初始化] 引脚设置为输出模式
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  out_mode:  BSP_GPIO_PIN_OUT_OD 开漏输出, BSP_GPIO_PIN_OUT_PP 推免输出, BSP_GPIO_PIN_AF_OD 复用开漏, BSP_GPIO_PIN_AF_PP 复用推免
 */
void bsp_gpio_init_output(void* gpiox, uint8_t pin, bsp_gpio_pin_out_t out_mode) {
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = pin,
        .Pull            = GPIO_NOPULL,
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,  // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH
    };
    switch (out_mode) {
        case BSP_GPIO_PIN_OUT_OD:
            gpio_init_struct.Mode       = GPIO_MODE_OUTPUT;
            gpio_init_struct.OpenDrain  = GPIO_OPENDRAIN;
            break;
        case BSP_GPIO_PIN_OUT_PP:
            gpio_init_struct.Mode       = GPIO_MODE_OUTPUT;
            gpio_init_struct.OpenDrain  = GPIO_PUSHPULL;
            break;
        case BSP_GPIO_PIN_AF_OD:
            gpio_init_struct.Mode       = GPIO_MODE_AF;
            gpio_init_struct.OpenDrain  = GPIO_OPENDRAIN;
            break;
        case BSP_GPIO_PIN_AF_PP:
            gpio_init_struct.Mode       = GPIO_MODE_AF;
            gpio_init_struct.OpenDrain  = GPIO_PUSHPULL;
            break;
        default:
            break;
    }
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&gpio_init_struct);
}

/**
 * @brief  [初始化] 引脚设置为输入模式
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  pull: BSP_GPIO_PIN_NOPULL 无上下拉, BSP_GPIO_PIN_PULLUP 上拉输入, BSP_GPIO_PIN_PULLDOWN 下拉输入
 */
void bsp_gpio_init_input(void* gpiox, uint8_t pin, bsp_gpio_pin_pull_t pull) {
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = pin,
        .Pull            = pull,
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,  // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,
        .Mode            = GPIO_MODE_INPUT,
    };
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&gpio_init_struct);
}

static uint8_t get_exti_type(bsp_gpio_exti_int_type_t exti_type) {
    if (BSP_GPIO_EXTI_INT_LEVEL == exti_type)
        return GPIO_EXTI_INT_LEVEL;
    else
        return GPIO_EXTI_INT_EDGE;
}

/**
 * @brief  [初始化] 引脚设置为[输入+中断]模式
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  irqn: 中断号（在typedef enum IRQn中，例如：USART1_IRQn）
 * @param  exti_type: BSP_GPIO_EXTI_INT_LEVEL 电平触发, BSP_GPIO_EXTI_INT_EDGE 边沿触发
 * @param  exti_event: BSP_GPIO_EXTI_INT_LOWFALL 低电平触发(下降沿), BSP_GPIO_EXTI_INT_HIGHRISE 高电平触发(上降沿), BSP_GPIO_EXTI_INT_FALLRISE 高低电平触发或任意电平变化
 * @param  pull: BSP_GPIO_PIN_NOPULL 无上下拉, BSP_GPIO_PIN_PULLUP 上拉输入, BSP_GPIO_PIN_PULLDOWN 下拉输入
 */
void bsp_gpio_init_input_exit(void* gpiox, uint8_t pin, uint8_t irqn, bsp_gpio_exti_int_type_t exti_type, bsp_gpio_exti_int_event_t exti_event, bsp_gpio_pin_pull_t pull) {
    /* Configure Button pin as input with External interrupt */
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = pin,
        .Pull            = pull,
        .Debounce.Enable = GPIO_DEBOUNCE_ENABLE,  // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,
        .Mode            = EXTI_MODE,
        .Exti.Enable     = GPIO_EXTI_INT_ENABLE,
    };
    gpio_init_struct.Exti.EdgeLevelSel  = get_exti_type(exti_type);
    gpio_init_struct.Exti.RiseFallSel   = get_exti_event(exti_event);
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&gpio_init_struct);
    bsp_exti_clear_flag(gpiox, pin);
    /* Enable and set Button EXTI Interrupt to the lowest priority */
    bsp_exti_set(irqn, PRIORITY_LOW);
}

/**
 * @brief  设置引脚电平状态
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  state: BSP_GPIO_PIN_RESET 低电平, BSP_GPIO_PIN_SET 高电平
 */
void bsp_gpio_set_pin(void* gpiox, uint8_t pin, bsp_gpio_pin_state_t state) {
    HAL_GPIO_WritePin(gpiox, pin, (GPIO_PinState)state);
}

/**
 * @brief  翻转引脚电平状态
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 */
void bsp_gpio_set_toggle(void* gpiox, uint8_t pin) {
    HAL_GPIO_TogglePin(gpiox, pin);
}

/**
 * @brief  得到指定gpio状态
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @retval 0 -- 低电平, 1 -- 高电平
 */
bool bsp_gpio_get_state(void* gpiox, uint8_t pin) {
    return (bool)HAL_GPIO_ReadPin(gpiox, pin);
}

/**
 * @brief  设置gpio时钟
 * @param  aphn: AHB外围x (High Speed APB)外围时钟。APB == 0  APB2 == 1
 * @param  periph: 时钟号
 */
void bsp_gpio_set_clk(bool aphn, uint32_t periph, bool state) {
    if (state)
        SET_BIT(RCC->HCLKEN, periph);
    else
        RCC->HCLKEN &= ~(periph);
}

/**
 * @brief  将指定引脚复用为ADC引脚（复用模拟输入）
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 */
void bsp_gpio_init_adc(void* gpiox, uint8_t pin) {
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = pin,
        .Mode            = GPIO_MODE_ANALOG,                  // 模拟输入
        .OpenDrain       = GPIO_OPENDRAIN,                    // 开漏输出
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,             // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,               // 电压转换速率
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,            // 驱动强度
        .Pull            = GPIO_NOPULL,                       // 无上下拉
    };
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&gpio_init_struct);
}

/**
 * @brief  初始化i2c引脚
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  arf: 复用值
 */
void bsp_gpio_init_i2c(void* gpiox, uint8_t pin, uint8_t arf) {
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = pin,
        .Mode            = GPIO_MODE_AF,           // GPIO端口复用功能
        .OpenDrain       = GPIO_OPENDRAIN,         // 开漏输出
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,  // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_LOW,     // 电压转换速率
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH, // 驱动强度
        .Pull            = GPIO_PULLUP,            // 上拉
        .Alternate       = arf
    };
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&gpio_init_struct);
}

/**
 * @brief  初始化uart引脚
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  arf: 复用值
 */
void bsp_gpio_init_uart(void* gpiox, uint8_t pin, uint8_t arf) {
    GPIO_InitTypeDef GPIO_InitStruct = {
        .Pin             = pin,
        .Mode            = GPIO_MODE_AF,
        .OpenDrain       = GPIO_PUSHPULL,
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,
        .SlewRate        = GPIO_SLEW_RATE_HIGH,
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,
        .Pull            = GPIO_PULLUP,
        .Alternate       = arf
    };
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&GPIO_InitStruct);
}

/**
 * @brief  初始化tim_ch引脚
 * @param  *gpiox: gpio组号（GPIOA/GPIOB/GPIOC/GPIOD等等）
 * @param  pin: 引脚号
 * @param  arf: 复用值
 */
void bsp_gpio_init_tim(void* gpiox, uint8_t pin, uint8_t arf) {
    GPIO_InitTypeDef GPIO_InitStruct = {
        .Pin             = pin,
        .Mode            = GPIO_MODE_AF,
        .OpenDrain       = GPIO_PUSHPULL,
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,
        .SlewRate        = GPIO_SLEW_RATE_HIGH,
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,
        .Pull            = GPIO_PULLUP,
        .Alternate       = arf
    };
    HAL_GPIO_Init(gpiox, (GPIO_InitTypeDef*)&GPIO_InitStruct);
}
